import io
import logging
from django.contrib.auth.models import User
from django_redis import get_redis_connection
from django.shortcuts import render
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework_simplejwt.views import TokenObtainSlidingView
from django.http import HttpResponse

from config.redisconfig import LOGIN_VERIFY_CODE_TEMPLATE, EXPIRE_TIME
from utils.verify import ImageVerify
from .models import UserDetail
from .serializer import MyTokenSerializer, UserDetailSerializer
from django.views import View
from rest_framework.viewsets import ModelViewSet, GenericViewSet  # 模型视图集，通用视图集
from rest_framework.mixins import RetrieveModelMixin, UpdateModelMixin  # 查单一
from .serializer import UserSerializer, UpdatePasswordSerializer
from rest_framework.status import HTTP_404_NOT_FOUND, HTTP_400_BAD_REQUEST, HTTP_500_INTERNAL_SERVER_ERROR
from utils.permissions import ActiveUserPermissions
from fdfs_client.client import Fdfs_client, get_tracker_conf
from config.fastdfs.avatar_config import FSATDFS_SERVER_DOMAIN

# Create your views here.

# 客户端配置，用于头像上传功能
client_conf = get_tracker_conf('config/fastdfs/client.conf')
client = Fdfs_client(client_conf)

# 导入日志器，用于上传头像出现异常
logger = logging.getLogger(__name__)
class MyTokenView(TokenObtainSlidingView):
    serializer_class = MyTokenSerializer
class ImageVerifyView(View):  # 图像验证码不需要mysql所以不用模型视图集
    def get(self, request, uuid):  # uuid是前端给我们传过来的
        """
        获取图形验证码
        :param EXPIRE_TIME:
        :param uuid:
        :param request:
        :return:
        """
        imgVerify = ImageVerify()  # 实例化得到一个对象
        img, code = imgVerify.image_code()  # 通过对象去调用方法

        # 图片以二进制形式进行传输
        # 创建管道，是字节流管道
        bytes_box = io.BytesIO()
        # 将图片以PNG格式保存到管道
        img.save(bytes_box, format='PNG')
        # 从字节流管道获取到的二进制图片
        image_bytes = bytes_box.getvalue()

        # code验证码保存到redis数据库
        cache = get_redis_connection(alias='verify_codes')  # 连接redis数据库，alias指定连接缓存配置的验证码配置，不写就是连接默认的配置
        cache.set(LOGIN_VERIFY_CODE_TEMPLATE % uuid, code, EXPIRE_TIME)  # 把验证码放到数据库里，验证码有key value，key要用到UUID

        return HttpResponse(image_bytes, content_type='image/png')
class UserViewSet(RetrieveModelMixin, GenericViewSet):
    #    """
    #    django view View  # 不需要返回json格式，返回字符串
    #    django drf ViewSet   # 不需要使用序列化器或者查询集
    #    django drf GenericViewSet+Mixin   # 需要使用序列化器或查询集
    #    django drf ModelViewSet  # 需要五种接口
    #    """
    queryset = User.objects.filter(is_active=1)  # 1为True, is_active为有是否是有效用户
    serializer_class = UserSerializer
    permission_classes = [ActiveUserPermissions]  # 权限设置

    def get_serializer(self, *args, **kwargs):
        """
        重写获取到的序列化器
        :param args:
        :param kwargs:
        :return:
        """
        if self.action == 'password':  # 如果当前的接口是password
            return UpdatePasswordSerializer(*args, **kwargs)
        return self.serializer_class(*args, **kwargs)

    def update(self, request, pk):
        """
        个人信息修改重写
        1.User表更新
            1.1通过PK查询user对象
            1.2拿到前端传入的数据request.data，反序列化
            1.3字段校验is_valid
            1.4更新
        2.UserDetail表更新
            2.1判断该用户是否有详情
            2.2有着获取，无着创建
            2.3拿到前端传入的数据request。data，反序列化
            2.4字段校验is_valid
            2.5更新
        :param request:
        :param pk: 用户id
        :return:
        """
        # 1.1 通过PK查询user对象
        try:
            user = User.objects.get(pk=pk)
        except User.DoesNotExist: # DoesNotExist为不存在
            return Response(status=HTTP_404_NOT_FOUND)
        # 1.2 拿到前端传入的数据request.data，反序列化
        user_serializer = self.get_serializer(user, data=request.data)
        # 1.3 字段校验is_valid
        user_serializer.is_valid(raise_exception=True)  # 抛出异常
        # 1.4 更新
        user_serializer.save()

        # 2.1 判断该用户是否有详情
        user_detail = request.data['userdetail']  # 从前端拿到的详情表数据
        if hasattr(user, 'userdetail'):
            # 2.2 有着获取，无着创建
            user_detail_serializer = UserDetailSerializer(user.userdetail, data=user_detail)
        else:
            user_detail_serializer = UserDetailSerializer(data=user_detail)
        # 2.4 字段校验is_valid
        user_detail_serializer.is_valid(raise_exception=True)
        # 2.5 更新
        user_detail_serializer.save()

        # 构造返回的数据构造
        user_serializer.data['userdetail'] = user_detail_serializer.data
        return Response(user_serializer.data, user_detail_serializer.data)

    @action(methods=['put'], detail=True)
    def password(self, request, pk):
        """
        新建密码接口，不是重写
        1.根据PK值查到用户对象
        2.拿到前端传入的数据：request.data,并反序列化
        3.字段校验
        4.判断提交的旧密码和数据库存放的是否一致
            正确，修改并保存
            错误，返回状态码，终止程序
        :param request:
        :param pk:
        :return:
        """
        # 1.根据PK值查到用户对象
        try:
            user = User.objects.get(pk=pk)
        except User.DoesNotExist:  # DoesNotExist为不存在
            return Response(status=HTTP_404_NOT_FOUND)
        # 2.拿到前端传入的数据：request.data，并反序列化
        password = request.data['password']
        new_password = request.data['new_password']
        serializer = self.get_serializer(data=request.data)
        # 3.字段校验
        serializer.is_valid(raise_exception=True)
        # 4.判断提交的旧密码和数据库存放的是否一致
        if user.check_password(password):
            # 正确，修改并保存
            user.set_password(new_password)
            user.save()
            return Response({'msg': '修改成功'})
        return Response(status=HTTP_400_BAD_REQUEST)

    @action(methods=['post'], detail=True)
    def avatar(self, request, pk):
        """
        头像上传功能
        1.根据PK获取用户信息
        2.获取传入的信息（头像图片文件：request.FILES）
        3.数据校验（文件数据和格式）
        4.上传到FastDFS
        5.将返回的地址储存到mysql数据（详情表）
        6.返回响应给前端
        :param request:
        :param pk:
        :return:
        """
        # 1.根据PK获取用户信息
        try:
            user = User.objects.get(pk=pk)
        except User.DoesNotExist:
            return Response(status=HTTP_404_NOT_FOUND)
        # 2.获取传入的信息（头像图片文件：request.FILES）
        files = request.FILES.get('file')
        # 3.数据校验（文件数据和格式）
        if not files or files.content_type not in('image/jpeg', 'image/png', 'image/gif', 'image/jpg'):
            return Response(status=HTTP_400_BAD_REQUEST)
        # 4.上传到FastDFS
        try:  # 此try为图片后缀名处理，图片为1.jpg
            image_ext_name = files.name.split('.')[-1]  # split分割
        except Exception as e:
            image_ext_name = 'png'
        try:  # 此try为上传文件异常处理
            res = client.upload_by_buffer(files.read(), file_ext_name=image_ext_name)  # 图片是二进制的
        except Exception as e:  # 将头像上传异常信息保存到日志器里
            logger.error(f'图片上传错误：{e}')
            return Response(status=HTTP_500_INTERNAL_SERVER_ERROR)
        else:
            if res.get('Status') != 'Upload successed.':
                return Response({'msg': '图片上传失败，请稍后重试或者联系管理员'}, status=HTTP_500_INTERNAL_SERVER_ERROR)
            else:
        # 5.将返回的地址储存到mysql数据（详情表）,需要对图片地址拼接，前面IP端口+后面图片地址
                image_url = FSATDFS_SERVER_DOMAIN + res.get('Remote file_id').decode()  # 二进制需解码
                if hasattr(user, 'userdetail'):  # 判断用户是否有详情
                    user_detail = user.userdetail  # 有则修改
                    user_detail.avatar = image_url
                    user_detail.save()
                else:                            # 无着创建
                    UserDetail.objects.create(avatar=image_url, user=user)
        # 6.返回响应给前端
                return Response({'data': image_url})